home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac100% 1998 November
/
MAC100-1998-11.ISO.7z
/
MAC100-1998-11.ISO
/
オンラインソフト定点観測
/
ユーティリティ
/
Mops 3.2.sea
/
Mops 3.2
/
Mops ƒ
/
Modules
< prev
next >
Wrap
Text File
|
1998-05-18
|
16KB
|
617 lines
(*
This file implements relocatable modules. In installed applications on
the 68k, these became separate code segments.
Modules live in separate files, and when needed, they're loaded into
a handle.
The management of modules is rolled into class Module - each module
we define gets a Module object which lives in the dictionary, and
handles the housekeeping details related to the module files.
Here's the module file format:
Header:
(offs 0 ) 4 bytes date/time compiled
(offs 4 ) 4 bytes DirID of source file
(offs 8 ) 4 bytes self-relative offset to exports table
(which follows the code)
(offs 12) 4 bytes code size
(offs 16) 4 bytes self-relative offset to data start
Code
Exports table:
(offs 0 ) 4 bytes offset from header start to first cfa
(offs 4 ) 4 bytes offset to next cfa
...
(offs n ) 4 bytes -1 marker for end of exports table
*)
true value CLEANMOD?
false value RELEASED?
0 value THIS_MOD
0 value LAST_MOD
0 value svDP
0 value evSvDP
0 value svLatest
0 value evSvLatest
0 value modstart
string $EXP
string $CXT
string $evCXT
¥ variable SAVE_CONTEXT 8 4 * allot
: UNEVAL ¥ Puts things back to normal after an EVAL"
evSvDP 0EXIT ¥ Out if we're not compiling an eval"
evSvLatest -> latest
evSvDP -> DP 0 -> evSvDP
nil?: $evCxt NIF ptr: $evCxt context 32 cmove release: $evCxt THEN
;
: UNMOD ¥ Puts things back to normal after a module
¥ or stand-alone code compilation or eval"
unEval
svDP 0EXIT ¥ Out if we're not compiling a module/SA
svLatest -> latest
svDP -> DP 0 -> svDP 0 -> compMod
nil?: $cxt NIF ptr: $cxt context 32 cmove release: $cxt THEN
false -> SAcomp? ;
: >NXTEXP ¥ ( cfa -- ) Adds the next cfa offset to the string $exp
¥ which will become the exports table.
modstart - pad ! pad 4 add: $exp ;
:class MODULE super{ object }
record
{ handle MODHDL
byte EXEC_CNT ¥ Must be at an even offset since we sometimes
bool LOCKED? ¥ do a combined access to exec_cnt and locked? !
byte FLAGS
int RES#
int #IMP
dicaddr LASTIMP
dicaddr LOADPOINT
var DicDateTime
int RELOFFS
int INSTALL?
}
:m BASE:
nil?: modHdl IF 0 EXIT THEN
nptr: modHdl ;m
:m HANDLE: get: modHdl ;m
:m .ID: ^base obj> .id ;m
:m SETRELEASE: ¥ ( addr -- )
modbase - put: relOffs ;m
:m SETRESID: ¥ ( resID -- )
put: res# ;m
:m INSTALL?: get: install? ;m
:m SETINSTALL: put: install? ;m
¥ KLUDGE: and UNKLUDGE: may be used when we save a dic image, to mark
¥ a module as unloaded in the saved image without really unloading it.
:m KLUDGE: ¥ ( -- modHdl flags exec+locked? )
get: modHdl get: flags addr: exec_cnt w@ nilH put: modHdl ;m
:m UNKLUDGE: ¥ ( modHdl flags exec+locked? -- )
addr: exec_cnt w! put: flags put: modHdl ;m
:m GETNAME: ¥ ( -- addr len )
^base obj> >name n>count ;m
:m EXTNAME: { xaddr xlen ¥ len -- addr' len' }
getName: self -> len pad len cmove
xaddr pad len + xlen cmove ¥ Add extension
pad len xlen + ;m
:m BINNAME: ¥ ( -- addr len ) Leaves name of binary file for module.
" .BIN" extName: self ;m
:m TXTNAME: ¥ ( -- addr len ) Leaves name of text file for module.
" .TXT" extName: self ;m
:m LOAD: { ¥ rc -- } ¥ Loads if not loaded already
nil?: modHdl 0EXIT
get: res#
IF 'type CODE get: res# getRes dup 0= ?error 138
put: modHdl
ELSE
binName: self name: fFcb 0 setVref: fFcb
openReadOnly: fFcb ?error 138
['] pause 4+ @ 0 -> pause ¥ Disable pause over read to avoid
¥ possible reentrancy
size: fFcb dup new: modHdl
lock: modHdl ¥ Maybe we need this
ptr: modHdl swap read: fFcb -> rc
['] pause 4+ ! ¥ Restore pause
unlock: modHdl ¥ Unlock before error check
close: fFcb drop rc ?error 141
base: self @ get: dicDateTime u<
IF ¥ BIN file is old version
release: modHdl 148 die
THEN
THEN
moveHi: modHdl ¥ Move module hi since it gets locked
clear: exec_cnt ;m
:m RELEASE: { ¥ svModbase -- }
clear: exec_cnt ¥ We certainly hope we know what we're
clear: locked? ¥ doing!!
get: modHdl nilH = ?EXIT ¥ Out if not loaded
get: relOffs -1 <> ¥ Any module-specific action?
IF ¥ Yes
lock: modHdl ¥ We're going to execute in the module
modbase -> svModbase
ptr: modHdl 32766 + dup -> modbase
get: relOffs +
execute ¥ Execute the appropriate word
svModbase -> modbase ¥ No need to unlock since we're
¥ just about to release
THEN
get: res# ¥ Resource?
IF
get: modHdl trap$ a9a3 ¥ call ReleaseResource
nilH put: modHdl
ELSE
release: modHdl
THEN
true -> released? ;m
(*
KEEP: and DROP: flag this module as needed and not needed, respectively.
The main purpose of this flagging is that if GETSPACE is called, loaded
modules will be released to make room, unless they have been flagged as
needed by KEEP:. But note that RELEASE: ignores the flag, so that we
can get rid of a module by force if necessary. This may happen if there
was a crash while the module was executing.
LOCK: is more drastic than KEEP:, since it means that this module becomes
non-relocatable. UNLOCK: reverses a LOCK:. Note that DROP: in effect does
an UNLOCK: as well.
This "locking" feature is used for ExtrasMod, which has a window, and
for the debugger and printMod, which can be entered through the back
door (via a vect or a trap). (By the way, we hope we won't have to do this
back door business anywhere else. Entering a module through the back door
is not usually a very safe thing to do.)
Locking a module can give a useful performance improvement if a module is to
be called several times in succession, since we bypass the _HLock and _Hunlock
calls if the module is marked locked.
*)
:m KEEP:
addr: flags 1 bset ;m
:m DROP:
get: exec_cnt NIF unlock: modHdl THEN ¥ Unlock if not executing
addr: flags 1 breset clear: locked? ;m
:m LOCK:
true put: locked? load: self lock: modHdl ;m
¥ Note: loading does a MoveHi so we don't need to do it again.
:m UNLOCK:
false put: locked?
get: exec_cnt NIF nil?: modHdl NIF unlock: modHdl THEN THEN ;m
:m KEEP?:
get: exec_cnt 0<> get: locked? or get: flags or ;m
:m LOCKED?:
get: exec_cnt get: locked? or ;m
:m ?RELEASE:
keep?: self ?EXIT
release: self ;m
:m #IMP: get: #imp ;m
:m GETIMPORTS: { ¥ n -- }
0 -> n
BEGIN
header -92 w, ¥ Header with handler code for imported word
^base compimp 1 ++> n
& } endlist?
UNTIL
n 1- put: #imp
latest name> put: lastimp
here put: loadpoint ;m
¥ ===================================
¥ Module compilation
¥ ===================================
private
:m ExpSupers: { ^nw -- }
BEGIN
^nw @ 0EXIT
^nw relocType InThisMod =
IF ^nw @abs 4+ ¥ get to start of methods area in class info
8 FOR ¥ go through the 8 method threads
dup displace i expMethods: [self]
4+
NEXT drop
THEN
4 ++> ^nw
AGAIN ;m
public
¥ This gets called via a late bind, so must be public
:m ExpMethods: { maddr thread# -- }
BEGIN ¥ Loop thru methods in this class
maddr @ 0>=
IF ¥ We've come to the superclasses - we only
¥ have to handle these once, of course - and
¥ since the order in the export table is
¥ immaterial, we'll just do it if we're on
¥ thread zero.
thread#
NIF maddr expSupers: self
THEN EXIT
THEN
¥ Next method
maddr 10 + ( cfa of method ) >nxtExp
maddr 4+ displace -> maddr
AGAIN ;m
private
mlocal !exports: { ¥ thisImp thisCfa maddr -- }
:m ?!class: ¥ If this exported item is a class, we set the handler
¥ code of the imported version and add the method entry offsets
¥ to the export table.
thisCfa 2- w@x -58 = 0EXIT ¥ Out if it isn't a class
-90 thisImp 2- w!
thisCfa ffa 1+ 1 bset
thisCfa 4+ ¥ get to start of methods area in class info
8 FOR ¥ go through the 8 method threads
dup displace i expMethods: self
4+
NEXT drop ;m
:m 1export:
next: theMark link> -> thisImp
thisImp >name n>count sFind
drop -> thisCfa
thisCfa thisImp =
IF ¥ Not defined
cr thisImp .id 2 spaces 144 die
¥ "You forgot to define this exported name"
false -> cleanMod?
ELSE ¥ All OK. Put info into import definition:
thisCfa >name c@ thisImp >name c! ¥ Name flags
pos: $exp thisImp 4+ w! ¥ Export table index
thisCfa >nxtExp ¥ Add next exp tbl entry
?!class: self ¥ More stuff if it's a class
THEN ;m
:mloc !exports: ¥ { ¥ n thisImp thisCfa maddr -- }
get: #imp 0= ?error 143 ¥ Module has no exported names
clear: $exp
get: lastimp set: theMark
get: #imp FOR 1export: self NEXT
-1 pad ! pad 4 add: $exp ¥ marker at end of table
;mloc
(*
FixLinks: fixes up the dictionary links within the compiled module. We may
want to find words in the module at run time via FIND, but the problem is that
dic links are relative, not relocatable. This makes FIND fast, but leads
to a problem at run time when the the module is disconnected from the main
dictionary. If we didn't do anything, we wouldn't know where to start
searching from, and if the search failed, the last link would point into
outer space.
So what we do is to add a snapshot of CONTEXT to the end of the module to give
a place to start from, and to clear the lowest link on each thread to zero (which
means the end).
*)
:m FixLinks: { ¥ link prevLink -- }
#threads FOR
context i cells + -> link
BEGIN
link -> prevLink
link displace -> link
link modstart u<
UNTIL
0 prevLink !
NEXT
here 4+ context - , ¥ Adjustment value for context copy
context 32 n, ¥ Add copy of Context to end of module
;m
:m GoodCompile: { ¥ size -- }
here modstart 8 + displ! ¥ Store export table offs in header
all: $exp n, ¥ Add export table to end
fixLinks: self ¥ fix dic links in module
here modstart - -> size ¥ Size of module
size modstart 12 + ! ¥ Store size in header
binName: self name: fFcb ¥ Set name of binary file
create: fFcb ?error 139
'type BIN 'type Mops set: fFcb ¥ Type and signature
modstart size write: fFcb ¥ Write out binary module
close: fFcb drop
IF msg# 140 ¥ I/O error on writing bin file
ELSE
curs -curs
cr getName: fFcb type ." saved" cr
-> curs
THEN
;m
public
:m COMPILE: { ¥ newModbase -- }
compMod ?error 177 ¥ Error if already compiling a module
release: self ¥ Get rid of old version, if loaded
context 32 put: $cxt ¥ save CONTEXT since we're going
¥ to do a temporary FORGET
DP -> svDP latest -> svLatest ^base -> compMod
get: loadpoint (forget) svDP -> DP
true -> cleanMod?
pushNew: loadFile
txtName: self name: topFile
here -> modstart
modstart 32766 + -> newModbase
16 reserve ¥ Reserve space for header and offset to exports table.
^base -> this_mod
newModbase LdFromMod
dateTime modstart ! ¥ Put source date in bin module header
getDirID: topFile modstart 4+ ! ¥ Also DirID of source file
drop: loadfile
0 -> this_mod
!exports: self
cleanMod?
IF goodCompile: self ¥ Everything's OK. Do final housekeeping
THEN
unmod ¥ Also releases $cxt
release: $exp ;m
¥ FIND: works like FIND, but just searches for a word in this module.
:m FIND: { s255 ¥ thrdOffs modCxt cxtOffs -- cfa T | -- s255 F }
load: self
s255 ¥ leave on stack for (find)
dup c@ 7 and 4* -> thrdOffs ¥ like what THREAD does
nptr: modHdl size: modHdl + 32 - -> modCxt
modCxt 4- @ -> cxtOffs
modCxt thrdOffs + displace
dup NIF ¥ thread is empty
drop false EXIT
THEN
cxtOffs -
( s255 1st-link ) (find)
;m
:m CLASSINIT:
-1 put: relOffs
dateTime put: dicDateTime ;m
;class
: SETRELEASE ¥ ( addr -- )
setRelease: [ this_mod ] ;
: MLD
dup load: ** ;
' mld -> modLoad
: MOD? ¥ ( cfa -- cfa b )
aligned_addr? NIF false EXIT THEN
dup >obj >classXt ['] module = ;
: ?DISP { theCfa size -- } ¥ handler to release selected modules
theCfa mod? NIF drop EXIT THEN
free size < ¥ Do we still need space?
IF >obj ?release: module
ELSE drop
THEN ;
¥ PURGE forcibly releases all modules, no matter what. It is a vector,
¥ defined in file Files.
: (PRG) { theCfa size -- } ¥ unlock and release
theCfa mod? NIF drop EXIT THEN
>obj release: module ;
: (PURGE) ['] (prg) big# trav ;
' (purge) -> purge
: NEEDSPACE ¥ ( #bytes -- ) release modules until #bytes are available
false -> released?
freeblk drop ['] ?disp swap trav ;
: GS big# needSpace released? ;
' gs -> getSpace
: FROM ¥ ( -- ^mod sec# )
module ¥ Create module object
latest name> >obj dup -> last_mod 28 ;
: IMPORT{ ¥ ( ^mod sec# -- )
28 ?pairs getImports: ** ;
: EXPORTS_CLASS
last_mod exports_class: ** ;
(* EVAL" ... " performs an EVALUATE on the quoted string, with one important
difference to EVALUATE - it temporarily returns the dictionary to the
state it was in when the EVAL" was compiled. This ensures that any
later redefinitions of words in the quoted string won't be used. This
is usually what you want. If you want redefinitions to be used, use
EVALUATE.
Note - we've put this definition here in the Modules file, since the
saving and restoring of the dictionary state is almost identical
to what has to be done during module compilation.
*)
: (EVAL")
context 32 put: $evCxt ¥ save CONTEXT since we're going
¥ to do a temporary FORGET
DP -> evSvDP latest -> evSvLatest
fence 0 -> fence ¥ disable fence check on (forget) since
¥ we might be in a module located below
¥ the dic in memory!
r> ¥ caller addr is where we forget to
dup (forget)
swap -> fence ¥ restore fence
evSvDP -> DP ¥ restore DP (but context is still forgotten)
count
2dup + aligned >r
unEval ¥ restore context etc.
evaluate ;
: EVAL"
postpone (eval")
," ¥ parse string delimited by " , add to dic
; immediate
(* ***
¥ Testing:
: QQ ." The right QQ!" cr ;
from TESTMOD import{ AA BB CC export_class }
: QQ ." This is the wrong QQ!!!" ; ¥ This one shouldn't!
compile: testmod
+echo
: h mword hash 0 db mfa_offset ;
: LOOKFOR Mword find: testmod ;
+echo
true -> classinitTest
export_class eee
endload
*** *)
¥ Now that's done, the next thing we need to do is set up our HFS file
¥ access:
from PATHSMOD import{ OWP GETPATHS .PATHS }
:f OPEN_WITH_PATHS OWP ;f
compile: pathsMod
true -> use_paths?
" mops.paths" getPaths
¥ Right, we now have HFS paths, so we can access our source files in
¥ different folders.
from CALL1&LMOD import{ CallFirst CallLast (GET) (C1) (CL) }
' (get) -> get1st&last
' (C1) -> doCall1st
' (CL) -> doCallLast
compile: call1&Lmod
0 value CASE_TYPE
from CASEMOD import{ case[ ]=> ], range]=> range], default=> ]case
select[ ]select }
compile: caseMod
: SELECT{ postpone select[ ; immediate
: }SELECT postpone ]select ; immediate
: IS{ postpone ]=> ; immediate
: }END postpone [ ; immediate
: DEFAULT{ postpone ] postpone default=> postpone drop ; immediate
from TOOL import{ CALL ASMCALL FCALL GLOBAL $>GLOB }
compile: tool
from CALLSMOD import{ SYSCALL KONST $>KONST }
compile: callsMod
from ASMMOD import{ ASM :CODE :MCODE TOCODE }
compile: asmmod
endload
¥ More testing stuff:
+echo
:class HAHA super{ int }
callLast print:
:m BAtest:
1 2 3 . . . ;m
;class
:class SUBHAHA super{ haha }
callLast dump:
:m BAtest: -9 -8 -7 . . . ;m
;class
haha hh
subhaha ss
: q db batest: hh batest: ss ;
: QQ ." QQ here. Hello. " ; ¥ This gets called from testMod
variable VB
compile: testmod2